unit uSBDUnit_HierarchyToLinearView;
interface
uses Generics.Collections, uSBD_ServiceProvider, uSBD_BaseTypes;
type

IGenericHierarchy = interface
  ['{5CB5B329-38EF-46A4-8447-8CBBF1FEDD91}']
    function Root: IInterface;
    function CountChildren( const Parent: IInterface): integer;
    function ChildOf( const Parent: IInterface; ChildIdx: integer): IInterface;
    function ParentOf( const Child: IInterface): IInterface;
    function CanonicalForm: TGUID;
  end;

TInterfaceEnumerable = TEnumerable<IInterface>;

TNodeFilter = reference to function(
  const Datum1: IInterface; Datum2: pointer; const Node: IInterface): boolean;

IHierarchyToLinearView_Converter = interface
  ['{846152C1-58FF-4836-A1F7-44A1BF0819D1}']
    function Enumerable(
      const Hierarchy: IGenericHierarchy;
      Filter: TNodeFilter;
      const Datum1: IInterface; Datum2: pointer
      ): TInterfaceEnumerable;
  end;



procedure RegisterServices( const Registrar: IServiceProvider);
// ^-- Registers (IHierarchyToLinearView_Converter,'')

implementation












uses SysUtils;

type
TCursorState = (cStart, cRunning, cEnd);
THierarchyToLinearView_Converter = class( TSBD_RefCountedCommonBase, IHierarchyToLinearView_Converter)
  private
    function Enumerable(
      const Hierarchy: IGenericHierarchy;
      Filter: TNodeFilter;
      const Datum1: IInterface; Datum2: pointer
      ): TInterfaceEnumerable;
  public
    [Configuration] constructor Create;
  end;

TIntfEnmerImpl = class( TInterfaceEnumerable)
  private
    FHierarchy: IGenericHierarchy;
    FFilter: TNodeFilter;
    FDatum1: IInterface;
    FDatum2: pointer;

  protected
    function DoGetEnumerator: TEnumerator<IInterface>; override;

  public
    constructor Create(
      const Hierarchy: IGenericHierarchy; Filter: TNodeFilter;
      const Datum1: IInterface; Datum2: pointer);
  end;

TIntfEnmorImpl = class( TEnumerator<IInterface>)
  protected
    FRoot: IInterface;
    FCursor: IInterface;
    FCursorState: TCursorState;
    FHierarchy: IGenericHierarchy;
    FFilter: TNodeFilter;
    FDatum1: IInterface;
    FDatum2: pointer;
    FCanonicalForm: TGUID;

    function DoGetCurrent: IInterface; override;
    function DoMoveNext: Boolean; override;

  public
    constructor Create(
      const Hierarchy: IGenericHierarchy; Filter: TNodeFilter;
      const Datum1: IInterface; Datum2: pointer);
  end;

procedure RegisterServices( const Registrar: IServiceProvider);
begin
THierarchyToLinearView_Converter
  .RegisterThis( Registrar, IHierarchyToLinearView_Converter)
end;


{ THierarchyToLinearView_Converter }

constructor THierarchyToLinearView_Converter.Create;
begin
end;

function THierarchyToLinearView_Converter.Enumerable(
  const Hierarchy: IGenericHierarchy; Filter: TNodeFilter;
  const Datum1: IInterface; Datum2: pointer): TInterfaceEnumerable;
begin
result := TIntfEnmerImpl.Create( Hierarchy, Filter, Datum1, Datum2)
end;

{ TIntfEnmerImpl }

constructor TIntfEnmerImpl.Create(
  const Hierarchy: IGenericHierarchy;
  Filter: TNodeFilter; const Datum1: IInterface; Datum2: pointer);
begin
FHierarchy := Hierarchy;
FFilter    := Filter;
FDatum1    := Datum1;
FDatum2    := Datum2
end;

function TIntfEnmerImpl.DoGetEnumerator: TEnumerator<IInterface>;
begin
result := TIntfEnmorImpl.Create( FHierarchy, FFilter, FDatum1, FDatum2)
end;

{ TIntfEnmorImpl }

constructor TIntfEnmorImpl.Create( const Hierarchy: IGenericHierarchy;
  Filter: TNodeFilter; const Datum1: IInterface; Datum2: pointer);
begin
FHierarchy   := Hierarchy;
FFilter      := Filter;
FDatum1      := Datum1;
FDatum2      := Datum2;
FRoot        := FHierarchy.Root;
FCanonicalForm := FHierarchy.CanonicalForm;
FCursor      := nil;
FCursorState := cStart
end;

function TIntfEnmorImpl.DoGetCurrent: IInterface;
begin
result := FCursor
end;

function TIntfEnmorImpl.DoMoveNext: Boolean;

  function Equals( const P1, P2: IInterface): boolean;
  var
    Canon1, Canon2: IInterface;
  begin
  result := (P1 = P2) or
            (Supports( P1, FCanonicalForm, Canon1) and
             Supports( P1, FCanonicalForm, Canon2) and
             (Canon1 = Canon2))
  end;

  function IndexOf( const Parent, Child: IInterface): integer;
  var
    Run: IInterface;
    j: integer;
  begin
  result := -1;
  if assigned( Parent) then
    for j := 0 to FHierarchy.CountChildren( Parent) - 1 do
      begin
      if not Equals( FHierarchy.ChildOf( Parent, j), Child) then continue;
      result := j;
      break
      end;
  end;

  function NextSibling: IInterface;
  var
    Parent: IInterface;
    Idx: integer;
  begin
  if assigned( FCursor) then
      Parent := FHierarchy.ParentOf( FCursor)
    else
      Parent := nil;
  Idx := IndexOf( Parent, FCursor);
  if (Idx >= 0) and (Idx <= (FHierarchy.CountChildren( Parent) - 2)) then
      result := FHierarchy.ChildOf( Parent, Idx + 1)
    else
      result := nil
  end;

begin
repeat
  case FCursorState of
    cStart:
      begin
      FCursorState := cRunning;
      FCursor      := FRoot;
      end;
    cRunning:
      begin
      if FHierarchy.CountChildren( FCursor) > 0 then
          FCursor := FHierarchy.ChildOf( FCursor, 0)
        else
          begin
          while assigned( FCursor) and (NextSibling = nil) do
            FCursor := FHierarchy.ParentOf( FCursor);
          if assigned( FCursor) then
              FCursor := NextSibling
            else
              FCursor := nil
          end
      end;
    cEnd:
      FCursor := nil
    end
until (not assigned( FCursor)) or (not assigned( FFilter)) or
      FFilter( FDatum1, FDatum2, FCursor);
result := assigned( FCursor);
if (not result) and (FCursorState <> cEnd) then
  FCursorState := cEnd
end;

end.
